﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.VirtualEarth.MapControl;
using System.IO;
using System.Xml.Linq;
using VEExtended;


namespace IKE
{
    public partial class MainPage : UserControl
    {
        private readonly Int32 PAGES_WITH_SEARCHED_TERM_IMPORTANCE = 100;
        private readonly Int32 WORDS_WEIGHT_IMPORTANCE = 100;
        private readonly Int32 MAX_RESULTS = 8;

        //vyraz zadany uzivatelem
        String searchedTerm = String.Empty;

        //id tohoto vyrazu
        String idOfSearchedTerm = String.Empty;

        //id stranky, na ktere je hledany term nejdulezitejsi
        Int32 bestPageIDForSearchedTerm = 0;

        //id stranek, na kterych je hledany term dulezity
        List<String> idsOfPagesWithSearchedTerm = new List<string>();

        //dulezite slov na strankech (viz prechozi)
        List<ResultWord> resultWordsIDs = new List<ResultWord>();

        // list termu pro predani do grafu
        List<Term> termList = new List<Term>();


        //pomocne iteratory pro prochezeni kolekci
        Int32 idsOfPagesWithSearchedTermIterator = 0;
        Int32 resultWordsIterator = 0;

        //promena pro predani id prave kliknuteho termu
        Int32 clickedTermID = 0;

        //indexove soubory a promenne indikujici, zda jsou tyto soubory zcela nacteny
        XDocument wordsIndexFile;
        Boolean isWordsIndexFileReady = false;
        XDocument pagesIndexFile;
        Boolean isPagesIndexFileReady = false;

        //ciselnik barev
        Color[] lovColors = {Color.FromArgb(255,176,23,31),
                                Color.FromArgb(255,238,106,167),
                                Color.FromArgb(255,191,62,255),	
                                Color.FromArgb(255,67,110,238),
                                Color.FromArgb(255,79,148,205),
                                Color.FromArgb(255,50,205,50),
                                Color.FromArgb(255,154,205,50),  	
                                Color.FromArgb(255,238,238,0),
                                Color.FromArgb(255,238,180,34),
                                Color.FromArgb(255,255,140,0),	
                                Color.FromArgb(255,238,44,44),
                                Color.FromArgb(255,176,176,176)};

        //index posledni pouzite barvy
        Int32 lastColorIndex = 0;

        MapLayer mp = new MapLayer();

        public MainPage()
        {
            InitializeComponent();

            LoadIndexFiles();

            graph.SetTileLayer(VEExtended.TileMap.Mode.Null); //mapy tam nechceme, vyklidím plochu
            graph.SetMapView(0, 0, 5);
        }

        private void graph_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            graph.SetBackground(); //maluju nabílo
        }

        //metoda vytvori z predaneho stringu odkaz na wiki
        private String CreateWikipediaURL(String s)
        {
            s.Replace(' ', '_');

            s = "http://cs.wikipedia.org/wiki/" + s;

            return s;
        }

        /***** 0. nacteni indexovych souboru slov a stranek *****/
        private void LoadIndexFiles()
        {
            WebClient wc = new WebClient();
            wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(wc_DownloadProgressChanged);
            wc.OpenReadCompleted += wc_OpenReadWordsXMLCompleted;
            wc.OpenReadAsync(new Uri("crawlerOutputFiles/Words.xml", UriKind.Relative));

            WebClient wc2 = new WebClient();
            wc2.OpenReadCompleted += wc_OpenReadPagesXMLCompleted;
            wc2.DownloadProgressChanged += new DownloadProgressChangedEventHandler(wc_DownloadProgressChanged);
            wc2.OpenReadAsync(new Uri("crawlerOutputFiles/Pages.xml", UriKind.Relative));
        }

        private void wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
            // Progress Updates
            MyProgress.Value = e.ProgressPercentage;
        }

        private void wc_OpenReadWordsXMLCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                graph.MessageNew(e.Error.Message);
                return;
            }
            try
            {
                using (Stream s = e.Result)
                {
                    wordsIndexFile = XDocument.Load(s);

                    isWordsIndexFileReady = true;
                }
            }
            catch (Exception ex) { graph.Message(ex.Message); }
        }

        private void wc_OpenReadPagesXMLCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                graph.MessageNew(e.Error.Message);
                return;
            }
            try
            {
                using (Stream s = e.Result)
                {
                    pagesIndexFile = XDocument.Load(s);

                    isPagesIndexFileReady = true;
                }
            }
            catch (Exception ex) { graph.Message(ex.Message); }
        }

        /***** 1. najde id prave vyhledavaneho termu *****/
        private void FindSearchedTermsId()
        {
            while (!isWordsIndexFileReady) { } //cekaci smyka pokud index jeste neni ready

            if (idOfSearchedTerm == String.Empty)
            {
                var query = from c in wordsIndexFile.Elements("Words").Elements("word")

                                where (string)c.Attribute("name").Value == searchedTerm

                                select c;

                    foreach (var el in query)
                    {
                        idOfSearchedTerm = el.Attribute("id").Value;
                    }

                    if (idOfSearchedTerm != String.Empty)
                    {
                        FindPagesForWordId();
                    }
                    else
                    {
                        //nebylo nalezeno
                        graph.MessageNew("Hledané slovo '" + searchedTerm + "' nebylo nalezeno");
                    }
            }
            else
            {
                //id uz zname, pokracujeme dal
                FindPagesForWordId();
            }
        }
        /****************************************************************************/

        /***** 2. najde vsechny stranky s vyhledavanym termem ******/
        private void FindPagesForWordId()
        {
            Int32 nidOfSearchedTerm = Convert.ToInt32(idOfSearchedTerm);
            Int32 indexOfPWFile = nidOfSearchedTerm / 50 + 1;

            //DOCASENE kvuli vzorku testovacich dat
            if (indexOfPWFile <= 6893)
            {
                WebClient wc = new WebClient();
                wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(wc_DownloadProgressChanged);
                wc.OpenReadCompleted += wc_OpenReadPWXMLCompleted;
                wc.OpenReadAsync(new Uri("crawlerOutputFiles/PagesWeight/pw" + indexOfPWFile.ToString() + ".xml", UriKind.Relative));
            }
            else
            {
                throw (new Exception("Hledané slovo nebylo nalezeno kvuli omezenemu vzorku dat"));
            }
        }

        private void wc_OpenReadPWXMLCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                graph.MessageNew(e.Error.Message);
                return;
            }
            try
            {
                using (Stream s = e.Result)
                {
                    XDocument xdoc = XDocument.Load(s);

                    var query = from c in xdoc.Elements("Words").Elements("word")

                                where (string)c.Element("id").Value == idOfSearchedTerm

                                select c;

                    foreach (var el in query)
                    {
                        var innerQuery = from d in el.Elements("page")

                                         where Convert.ToInt32(d.Element("weight").Value) > PAGES_WITH_SEARCHED_TERM_IMPORTANCE

                                         select d;

                        foreach (var iel in innerQuery)
                        {
                            idsOfPagesWithSearchedTerm.Add(iel.Element("id").Value);
                            
                            //hledame id s maximalni vahou jako vyslednou stranku pro hledany term
                            //neboli id stranky, na ktere je tento term nejdulezitejsi
                            if (Convert.ToInt32(iel.Element("weight").Value) > bestPageIDForSearchedTerm)
                            {
                                bestPageIDForSearchedTerm = Convert.ToInt32(iel.Element("id").Value);
                            }
                        }
                    }

                    if (idsOfPagesWithSearchedTerm.Count != 0)
                    {
                        FindWordsWithWeightsForPages();
                    }
                    else
                    {
                        //slovo neni na zadnych strankach (vzhledem k nastavene dulezitosti)
                        throw (new Exception("Hledané slovo '" + searchedTerm + "' nebylo nalezeno"));
                    }
                }
            }
            catch (Exception ex) { graph.MessageNew(ex.Message); }
        }
        /****************************************************************************/

        /****** 3. najde všechny důležitá slova na stránkách *****/
        private void FindWordsWithWeightsForPages()
        {
            Int32 idOfPage = Convert.ToInt32(idsOfPagesWithSearchedTerm[idsOfPagesWithSearchedTermIterator]);
            Int32 indexOfWOPFile = idOfPage / 100 + 1;

            //DOCASNE - testovaci soubory jsou omezene
            if (indexOfWOPFile <= 149)
            {
                WebClient wc = new WebClient();
                wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(wc_DownloadProgressChanged);
                wc.OpenReadCompleted += wc_OpenReadWOPXMLCompleted;
                wc.OpenReadAsync(new Uri("crawlerOutputFiles/WordsOnPages/wop" + indexOfWOPFile.ToString() + ".xml", UriKind.Relative));
            }
            else
            {
                //protoze stranky jsou podle velikosti, vsechny dalsi uz budou taky vetsi nez 51, tzn.
                // muzeme smele pokrocit dal

                ManageResults();
            }
        }

        private void wc_OpenReadWOPXMLCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                graph.MessageNew(e.Error.Message);
                return;
            }
            try
            {
                using (Stream s = e.Result)
                {
                    XDocument xdoc = XDocument.Load(s);

                    String currentPageID = idsOfPagesWithSearchedTerm[idsOfPagesWithSearchedTermIterator];

                    var query = from c in xdoc.Elements("Pages").Elements("Page")

                                where (string)c.Element("id").Value == currentPageID

                                select c;

                    foreach (var el in query)
                    {
                        var innerQuery = from d in el.Elements("Word")

                                         where Convert.ToInt32(d.Element("weight").Value) > WORDS_WEIGHT_IMPORTANCE

                                         select d;

                        foreach (var iel in innerQuery)
                        {
                            ResultWord word = new ResultWord();
                            word.wordID = iel.Element("id").Value;
                            word.weight = iel.Element("weight").Value;
                            word.pageID = currentPageID;

                            resultWordsIDs.Add(word);
                        }
                    }

                    idsOfPagesWithSearchedTermIterator++;

                    if (idsOfPagesWithSearchedTermIterator < idsOfPagesWithSearchedTerm.Count)
                    {
                        //dalsi stranka
                        FindWordsWithWeightsForPages();
                    }
                    else
                    {
                        ManageResults();
                    }
                }
            }
            catch (Exception ex) { graph.Message(ex.Message); }
        }
        /****************************************************************************/

        /***** 4. zkontroluje kolik je vysledku a pripadne vybere pouze ty nejdulezitejsi *****/
        private void ManageResults()
        {
            //setridime vysledky
            resultWordsIDs.Sort();

            //nyni kolekci vysledku zmensime tak, ze:
            // - vybereme pouze MAX_RESULT vysledku
            // - odstranime vsechny slova stejna jako hledane
            List<ResultWord> topResultsWithNoWordSameAsSearched = new List<ResultWord>();
            
            //prochazime kolekci on nej vysledku po nejhorsi
            for (int i = resultWordsIDs.Count - 1; i >= 0; i--)
            {
                //jestlize to neni hledany term, pak ho bereme //TODO: odstranit stejne
                if (resultWordsIDs[i].wordID != idOfSearchedTerm)
                {
                    topResultsWithNoWordSameAsSearched.Add(resultWordsIDs[i]);
                }

                //jestlize jich mame tolik, kolik jsme chteli, pak koncime cyklus
                if (topResultsWithNoWordSameAsSearched.Count == MAX_RESULTS)
                {
                    break;
                }
            }

            resultWordsIDs = topResultsWithNoWordSameAsSearched;

            if (clickedTermID == 0) //kliknulo se znova na koren nebo je to prvni hledani
            {
                ResultWord root = new ResultWord();
                root.wordID = idOfSearchedTerm;
                root.pageID = bestPageIDForSearchedTerm.ToString();
                root.weight = "-1"; //mala vaha zajisti, ze bude koren v kolekci po setrideni jako prvni

                resultWordsIDs.Add(root);

                resultWordsIDs.Sort();
            }

            AddPagesNames();
        }
        /****************************************************************************/

        /***** 5. najde a doplni jmena stranek *****/
        private void AddPagesNames()
        {
            while (!isPagesIndexFileReady) { }

            foreach (ResultWord word in resultWordsIDs)
            {
                var query = from c in pagesIndexFile.Elements("Pages").Elements("Page")

                            where  c.Element("id").Value == word.pageID

                select c;

                foreach (var el in query)
                {
                    word.pageName = el.Element("name").Value;
                }
            }

            CreateTerms();
         }
        /****************************************************************************/
    
        /***** 6. vytvori termy a vlozi do grafu *****/
        private void CreateTerms()
        {
            foreach (ResultWord word in resultWordsIDs)
            {
                Random random = new Random();
                Int32 colorIndex = random.Next(lovColors.Length);

                while (colorIndex == lastColorIndex)
                {
                    colorIndex = random.Next(lovColors.Length);
                }

                lastColorIndex = colorIndex;

                var query = from c in wordsIndexFile.Elements("Words").Elements("word")

                where (string)c.Attribute("id").Value == word.wordID

                select c;

                foreach (var el in query)
                {
                    Term t = new Term();

                    t.WordID = Convert.ToInt32(el.Attribute("id").Value);

                    t.Name = el.Attribute("name").Value;

                    t.Bubble = new Bubble();
                    t.Bubble.Color = lovColors[lastColorIndex];
                    t.Bubble.Size = 10;

                    t.Line = new VEExtended.Line();
                    t.Line.Color = lovColors[lastColorIndex];
                    t.Line.Size = 5;
                                                         
                    t.Text = word.pageName;
                    t.URL = CreateWikipediaURL(word.pageName);

                    if (word.weight == "-1")
                    {
                        t.RootID = 0; //jestlize je to root, pak ukazuje na termID 0 (tj. sam na sebe)

                        //zaroven jeho udaje doplnime do tabulky
                        tbText.Text = t.Text;
                        tbName.Text = t.Name;
                        hbLink.Content = t.URL;
                        if (t.URL != null)
                            hbLink.NavigateUri = new Uri(@t.URL);
                    }
                    else
                    {
                        t.RootID = clickedTermID; //jinak je root ten term, na ktery jsme kliknuli
                    }
                            
                    termList.Add(t);
                 }
            }
                    
            graph.AddTermListToGraph(termList); 
            graph.RepaintGraph();

            tbSearch.Text = "";
               
        }
        /****************************************************************************/

        private void btnSearch_Click(object sender, RoutedEventArgs e)
        {
            if (tbSearch.Text != String.Empty)
            {
                graph.RemoveTermsFromGraph();// vycistit kolekci, vymazat graf

                idOfSearchedTerm = String.Empty;
                idsOfPagesWithSearchedTerm = new List<string>();
                resultWordsIDs = new List<ResultWord>();
                termList = new List<Term>();

                clickedTermID = 0;

                idsOfPagesWithSearchedTermIterator = 0;
               
                String[] searchedTerms = tbSearch.Text.Split(' ');

                searchedTerm = searchedTerms[0];
                FindSearchedTermsId();
            }
        }

        private void graph_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            //Po kliknutí na Term načtu jeho data a vypíšu do levého sloupku
            Term tt = graph.getTerm(graph.activeTerm);
            if (tt.WordID != Convert.ToInt32(idOfSearchedTerm)) //neni to ten, ktery jsme hledali minule
            {
                tbText.Text = tt.Text;
                tbName.Text = tt.Name;
                hbLink.Content = tt.URL;
                if (tt.URL != null)
                    hbLink.NavigateUri = new Uri(@tt.URL);

                //pro koren uz znova nehledame
                if (tt.TermID != 0)
                {
                    searchedTerm = tt.Name;
                    idOfSearchedTerm = tt.WordID.ToString();
                    clickedTermID = tt.TermID;

                    idsOfPagesWithSearchedTerm = new List<string>();
                    resultWordsIDs = new List<ResultWord>();
                    termList = new List<Term>();

                    idsOfPagesWithSearchedTermIterator = 0;

                    FindSearchedTermsId();
                }
            }
        }

        private void btnPok_Click(object sender, RoutedEventArgs e)
        {
            graph.RemoveTermsFromGraph(); // vycistit kolekci, vymazat graf

            /*Term tt = new Term();
            tt.WordID = 15;
            tt.RootID = 9;
            tt.Name = "Nove pridane";
            tt.Text = "Já jsem nový termík";
            Bubble bub = new Bubble() { Color = Colors.Magenta, Size = 8 };
            tt.Bubble = bub;
            VEExtended.Line line = new VEExtended.Line() { Color = Colors.LightGray, Size = 1 };
            tt.Line = line;
            graph.AddTermToGraph(tt);*/
        }

        private void btnPok2_Click(object sender, RoutedEventArgs e)
        {
            graph.RepaintGraph(); //překreslení všech Termů v grafu
        }

        ///Mohlo by se hodit:
        ///
        /// public void AddTermListToGraph(List<Term> termList) - vloží list termů do grafu všechny hned vykreslím
        /// public void AddTermToGraph(Term term) - vloží jeden term do grafu
        /// public void RemoveTermFromGraph(Term term) - odeberu term z grafu
        /// public Int32 deepTerm(Int32 ID, Int32 num) - vrací hloubku(zanoření) termu z grafu ID = ID termu, num = dejte nulu (rekurzivně zvyšuju)
        /// public Term getTerm(Int32 ID) - vrací term podle ID
        /// public void RepaintGraph() - překreslí graf (vykreslí všechny termy v grafu)
        /// public void PaintTermChild(Term term) - vykreslí všechny potomky kořenového termu (např. používám jako onClick na term
        /// public void PaintTerm(Term term) - vykreslím term (neuloží se do grafu), nutné mít zadané souřadnice - Location, jinak vykreslí na 0,0
        /// 
        /// nějaké dotazy? :)
        /// 

        /*
      private void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
      {
          if (e.Error != null)
          {
              graph.MessageNew(e.Error.Message);
              return;
          }
          try
          {
              using (Stream s = e.Result)
              {
                  XDocument xdoc = XDocument.Load(s);

                  //Vytvořím seznam termů načtených z XML
                  List<Term> termList =
                      (from term in xdoc.Descendants("Term")
                       select new Term()
                       {
                           ID = Convert.ToInt32(term.Element("ID").Value),
                           RootID = Convert.ToInt32(term.Element("RootID").Value),

                           Name = term.Element("Name").Value,
                           Text = term.Element("Text").Value,
                           URL = term.Element("URL").Value,

                           Bubble = new Bubble()
                           {
                               Color = new Converter().ColorFromString(term.Element("Bubble").Attribute("Color").Value),
                               //   Location = new Converter().LocationFromString(term.Element("Bubble").Attribute("Location").Value),
                               Size = Convert.ToDouble(term.Element("Bubble").Attribute("Size").Value)
                           },
                           Line = new VEExtended.Line()
                           {
                               Color = new Converter().ColorFromString(term.Element("Line").Attribute("Color").Value),
                               Size = Convert.ToDouble(term.Element("Line").Attribute("Size").Value)
                           }
                       }).ToList();
                  //A pak je šoupnu do grafu
                  graph.AddTermListToGraph(termList); //šoupnu
                  graph.PaintTerm(termList[0]); //vykreslím první(kořenový)
                  //graph.RepaintGraph(); - kdybych chtěl vykreslit celý graf najednou
              }
          }
          catch (Exception ex) { graph.Message(ex.Message); }
      }*/


    }

}
